package source.events;

import java.util.*;

/**
 * The EventHandler class is responsible for controlling the flow
 * of events from the time that they are fired to the point at which
 * they are handled.  Each subclass of Event requires its own
 * EventHandler, and all valid listeners for a particular event must
 * first registered with the handler.
 * 
 * @author Brent
 *
 * @param <E> The subclass of Event the object will handle
 */

public class EventHandler<E extends Event> extends Thread {
	private ArrayList<EventListener<E>> listeners;
	private ArrayList<E> eventHistory;
	private Queue<E> eventQueue;
	private boolean eventsPending;
	private boolean handlingEvents;
	
	/**
	 * Sets up a new event handler, and starts its event processing
	 * thread.
	 *
	 */
	public EventHandler()
	{
		listeners = new ArrayList<EventListener<E>>();
		eventHistory = new ArrayList<E>();
		eventQueue = new LinkedList<E>();
		handlingEvents = true;
		
		//start();
	}
	
	/**
	 * Registers a new listener with the EventHandler
	 * 
	 * @param el - The new listener to be registered
	 */
	public void registerListener(EventListener<E> el)
	{
		listeners.add(el);
	}
	
	/**
	 * Removes a listener from the EventHandler
	 * 
	 * @param el - the listener to be removed
	 * @return true if and only if the listener was removed
	 */
	public boolean unRegisterListener(EventListener<E> el)
	{
		return listeners.remove(el);
	}
	
	/**
	 * Asynchronously fires an event. This is the prefered method
	 * of firing events.
	 * 
	 * @param e - The event to be fired
	 */
	public boolean fireEvent(E e)
	{
		if(handlingEvents)
		{
			dispatchEvent(e);
			return e.handled;
		} else return false;
	}
	
	/**
	 * Immeadiatley (synchronously) fires an event.  Guaruntees the
	 * event is fired right away, but may slow the execution of the
	 * calling thread (especially if there are alot of listeners)
	 * 
	 * @param e - The event to be fired
	 * @return true if and only if all listeners responded sucessfully
	 */
	public boolean fireEventNow(E e)
	{
		if(handlingEvents)
		{
			dispatchEvent(e);
			return e.handled;
		} else return false;
	}
	
	/**
	 * Used to start the event dispatching thread.  This method is
	 * automatically started by the constructor, and should not be
	 * called directly.
	 */
	public void run() 
	{ 
		while(handlingEvents) 
		{ 
			if(eventsPending) dispatchEvent(eventQueue.poll()); 
			else 
			try { 
				Thread.sleep(500); 
			} catch (InterruptedException e) { 
//	 TODO Auto-generated catch block 
				e.printStackTrace(); 
			} 
		} 
	}
	
	/**
	 * Terminates the event handling thread for this event handler,
	 * and stops it from handling any incoming events. Once terminated,
	 * an EventHandler becomes useless.  Used to stop the EventHandling
	 * thread once program execution ends.
	 *
	 */
	public void terminate()
	{
		handlingEvents = false;
	}
	
	/**
	 * Used to check if the EventHandler is handling events. Returns false
	 * if the EventHandler has been terminated.
	 * 
	 * @return true if the EventHandler is active
	 */
	public boolean isActive()
	{
		return handlingEvents;
	}
	
	/**
	 * @param filename: String
	 */
	
	public boolean dumpEventLog(String filename)
	{
		try
		{
			java.io.FileOutputStream outFile = new java.io.FileOutputStream("EventLog.txt");
			java.io.PrintWriter out = new java.io.PrintWriter(outFile);
			
			out.println("** BEGIN EVENT LOG **");
			
			for(E e : eventHistory)
			{
				out.println(e);
			}
			
			out.println("** END EVENT LOG **");
			
			out.close();
			
			return true;
			
		}catch (java.io.IOException e)
		{
			return false;
		}
	}
	
	/**
	 * Dispatches the provided event.
	 * 
	 * @param e - event to be dispatched
	 */
	private void dispatchEvent(E e)
	{
		boolean handled = true;
		for(EventListener<E> el : listeners)
		{
			if(!el.eventCallback(e)) handled = false;
		}
		e.handled = handled;
		
		eventHistory.add(e);
		
		if (eventQueue.peek() == null) eventsPending = false;
	}
	
}
